using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Diagnostics;

namespace Microsoft.Samples.BizTalk.Adapter.Tcp
{
    /// <summary>
    ///     This class provides a basic abstract implementation of a worker thread.
    /// </summary>
    /// <author>
    ///     Paul Brown
    /// </author>
    public abstract class WorkerThread : IWorkerThread, IDisposable
    {
        #region Private Instance Members

        private Queue<IWorkItem>        _workItems;     // The queue of work items of this worker thread
        private object                  _queueLock;     // The lock object for the queue
        private Thread                  _mainThread;    // The main thread that processes the work
        private bool                    _isStopping;    // Determines whether the worker thread is stopping
        private bool                    _isStopped;     // Determines whether the worker thread is currently stopped.
        private bool                    _isDisposed;    // A flag that determines whether the object has been disposed of
        private string                  _namePrefix;    // The prefix used to apply to the name of the thread
        private IAdapter                _adapter;       // The adapter that the worker thread is associated with
        private AutoResetEvent          _workSignal;    // This is signalled when work has arrived
        private Stopwatch               _idleStopwatch; // This stopwatch measures thread idle time
        private bool                    _idleTerminated;// Indicates whether the thread was idle terminated if stopped

        #endregion

        #region Constructor

        /// <summary>
        ///     This is the default constructor.
        /// </summary>
        public WorkerThread(IAdapter adapter)
        {
            // Validate
            if (adapter == null) throw new ArgumentNullException("adapter");

            // Initialize members
            this._isDisposed = false;
            this._isStopping = false;
            this._isStopped = true;
            this._workItems = new Queue<IWorkItem>();
            this._queueLock = new object();
            this._mainThread = null;
            this._namePrefix = string.Empty;
            this._adapter = adapter;
            this._workSignal = new AutoResetEvent(false);
            this._idleStopwatch = new Stopwatch();
            this._idleTerminated = false;
        }

        #endregion

		#region Finalizer

		/// <summary>
		///     This method is the finalizer for the class.  It uses the same syntax as
		///     a C++ destructor but is not deterministically destroyed.  By using this
		///     syntax, the compiler automatically emits a try-finally block around the
		///     code to ensure the base class is disposed of as well.
		/// </summary>
		~WorkerThread()
		{
			// Call protected implementation of Dispose
			this.Dispose(false);
		}

		#endregion

        #region Public Instance Events

        /// <summary>
        ///     This event is fired just before the worker thread terminates.
        /// </summary>
        public event EventHandler ThreadTerminated;

        #endregion

        #region Protected Instance Methods

        /// <summary>
        ///     This method invokes the ThreadTerminated event.
        /// </summary>
        protected virtual void OnThreadTerminated(EventArgs e)
        {
            if (this.ThreadTerminated != null)
            {
                this.ThreadTerminated(this, e);
            }
        }

        /// <summary>
        ///     This method enqueues work in this worker thread for dequeuing later.
        /// </summary>
        protected virtual void EnqueueWork(IWorkItem workItem)
        {
            lock (this._queueLock)
            {
                this._workItems.Enqueue(workItem);
            }
        }

        /// <summary>
        ///     This method dequeues work in this worker thread.
        /// </summary>
        protected virtual IWorkItem DequeueWork()
        {
            lock (this._queueLock)
            {
                return (this._workItems.Dequeue());
            }
        }

        /// <summary>
        ///     This method pushes a work item back onto the head of the queue.
        /// </summary>
        protected virtual void PushWorkOnQueue(IWorkItem workItem)
        {
            lock (this._queueLock)
            {
                // Copy items out into separate array
                IWorkItem[] workItems = new IWorkItem[this._workItems.Count];
                this._workItems.CopyTo(workItems, 0);

                // Clear the queue
                this._workItems.Clear();

                // Add pushed item to queue
                this._workItems.Enqueue(workItem);

                // Add all items from array back into queue
                foreach (IWorkItem item in workItems)
                {
                    this._workItems.Enqueue(item);
                }
            }
        }

        /// <summary>
        ///     This method waits for work to arrive for the thread.
        /// </summary>
        protected virtual void WaitForWork()
        {
            // Start idle timer
            this.StartIdleTimer();

            // Wait
            this._workSignal.WaitOne();

            // If we get here, work has arrived so stop idle timer
            this.StopIdleTimer();
        }

        /// <summary>
        ///     This method provides a private implementation which a flag that specifies whether we
        ///     are manually disposing of the object, or is the CLR disposing of it for us.
        /// </summary>
        protected virtual void Dispose(bool disposing)
        {
            // Only dispose if we are not already
            if (!this.IsDisposed)
            {
                // If we have been GC collected, the CLR may have already disposed of other object references
                // so it may not be possible to clean them up.  If we are manually disposed, we can dispose
                // of our objects.
                if (disposing)
                {
                    // If we are not stopped or stopping, then stop the worker thread
                    if (this.IsStopping == false && this.IsStopped == false)
                    {
                        this.Stop();
                    }
                }

                // Set flag
                this._isDisposed = true;
            }
        }

        /// <summary>
        ///     This method is the delegate for the thread that performs the work in the queue.  It must be overridden
        ///     in a derived class.
        /// </summary>
        protected abstract void Thread();

        #endregion

        #region Protected Instance Properties

        /// <summary>
        ///     This property gets or sets the thread name prefix and is used by a derived type to set the prefix
        ///     for this worker thread prior to the worker thread being started so it gets a unique name for the different
        ///     types of worker threads.
        /// </summary>
        protected string ThreadNamePrefix
        {
            get { return (this._namePrefix); }
            set { this._namePrefix = value; }
        }

        #endregion

        #region Private Instance Methods

        /// <summary>
        ///     This method starts the idle timer and should be used once the thread has handled all work and becomes idle.
        /// </summary>
        private void StartIdleTimer()
        {
            // Start counter
            this._idleStopwatch.Start();
        }

        /// <summary>
        ///     This method stops the idle timer and should be used when work has arrived for the thread.
        /// </summary>
        private void StopIdleTimer()
        {
            // Stop counter
            this._idleStopwatch.Reset();
        }

        #endregion

        #region IWorkerThread Members

        /// <summary>
        ///     This property returns whether the object has been disposed of.
        /// </summary>
        public bool IsDisposed
        {
            get { return (this._isDisposed); }
        }

        /// <summary>
        ///     This property returns whether the worker thread is currently stopping.
        /// </summary>
        public bool IsStopping
        {
            get { return (this._isStopping); }
        }

        /// <summary>
        ///     This property returns whether the worker thread is currently in a stopped state.
        /// </summary>
        public bool IsStopped
        {
            get { return (this._isStopped); }
        }

        /// <summary>
        ///     This property returns whether the worker thread is idle or not.
        /// </summary>
        public bool IsIdle
        {
            get { return (this._workItems.Count == 0 ? true : false); }
        }

        /// <summary>
        ///     This property returns the time that a thread has been idle.
        /// </summary>
        public TimeSpan IdleTime
        {
            get { return (this._idleStopwatch.Elapsed); }
        }

        /// <summary>
        ///     This property returns whether the worker thread was terminated due to being idle if it has stopped.
        /// </summary>
        public bool HasIdleTerminated
        {
            get { return (this._idleTerminated); }
        }

        /// <summary>
        ///     This property returns the current workload of the thread.
        /// </summary>
        public int CurrentWorkLoad
        {
            get { return (this._workItems.Count); }
        }

        /// <summary>
        ///     This property returns the name of the worker thread.
        /// </summary>
        public string Name
        {
            get
            {
                if (this._mainThread == null)
                {
                    return (string.Empty);
                }
                return (this._mainThread.Name);
            }
        }

        /// <summary>
        ///     This property returns the adapter that the worker thread is associated with.
        /// </summary>
        public IAdapter Adapter
        {
            get { return (this._adapter); }
        }

        /// <summary>
        ///     This property should be overridden in a derived class.
        /// </summary>
        public abstract Type WorkItemType
        {
            get;
        }

        /// <summary>
        ///     This method starts the worker thread by spawning off a new thread.
        /// </summary>
        public virtual void Start()
        {
            // Create thread
            this._mainThread = new Thread(new ThreadStart(this.Thread));

            // Set name of thread
            string name = this.ThreadNamePrefix + this._mainThread.ManagedThreadId.ToString();

            // Start a new thread
            if (this.Adapter.TraceSwitch.TraceVerbose)
                TraceHelper.WriteEntry(TraceLevel.Verbose, ResourceConstants.WorkerThreadStarting, name);

            // Start a new thread
            this._mainThread.IsBackground = true;
            this._mainThread.Name = name;
            this._mainThread.Start();

            // Thread started, set flags
            this._isStopping = false;
            this._isStopped = false;

            if (this.Adapter.TraceSwitch.TraceVerbose)
                TraceHelper.WriteEntry(TraceLevel.Verbose, ResourceConstants.WorkerThreadStarted, name);
        }

        /// <summary>
        ///     This method stops the worker thread.  It will block until it is complete.
        /// </summary>
        public virtual void Stop()
        {
            this.Stop(false);
        }

        /// <summary>
        ///     This method stops the worker thread and indicates to the method whether this
        ///     was a stop in response to the thread idling and being terminated by the dispatcher.
        ///     It will block until it is complete.
        /// </summary>
        public virtual void Stop(bool idleTerminated)
        {
            // Stop the thread
            if (!this.IsStopped)
            {
                if (this.Adapter.TraceSwitch.TraceVerbose)
                    TraceHelper.WriteEntry(TraceLevel.Verbose, ResourceConstants.WorkerThreadStopping, this._mainThread.Name);

                // Asked to stop, set flags
                this._isStopping = true;
                this._isStopped = false;
                this._idleTerminated = idleTerminated;

                // Shut down thread
                if (this._mainThread.ThreadState != System.Threading.ThreadState.Aborted &&
                    this._mainThread.ThreadState != System.Threading.ThreadState.Stopped)
                {
                    lock (this._queueLock)
                    {
                        // Wait for thread to finish processing
                        while (this._workItems.Count > 0) ;

                        // Interrupt thread to stop it
                        this._mainThread.Interrupt();
                    }
                }

                // Thread stopped, set flags
                this._isStopped = true;
                this._isStopping = false;

                if (this.Adapter.TraceSwitch.TraceVerbose)
                    TraceHelper.WriteEntry(TraceLevel.Verbose, ResourceConstants.WorkerThreadStopped, this._mainThread.Name);
            }
        }

        /// <summary>
        ///     This method queues up work for the main thread to process.
        /// </summary>
        public virtual void DoWork(IWorkItem workItem)
        {
            // Is the thread stopped or stopping?
            if (this.IsStopping) throw new WorkerThreadStoppingException(this.Name);
            if (this.IsStopped) throw new WorkerThreadStoppedException(this.Name);

            // Enqueue work item
            this.EnqueueWork(workItem);

            // Signal that there is work
            this._workSignal.Set();
        }

        #endregion

        #region IDisposable Members

        /// <summary>
        ///     This method disposes of the object manually.
        /// </summary>
        public void Dispose()
        {
            // Dispose
            this.Dispose(true);

            // Suppress finalize as we have already cleaned it up
            GC.SuppressFinalize(this);
        }

        #endregion
    }
}
